/**
 * GUI Widget: Screen 界面的小组件对象。
 * 创建指南：
 * 1. 新组件继承 GWidget；
 * 2. 编写获取新组件宽度的函数 uint16_t (*get_width) (GWidget *)；
 * 3. 编写获取新组件高度的函数 uint16_t (*get_height)(GWidget *)；
 * 4. 编写在区域内渲染本新组件的函数 void (*render)(GWidget *, uint16_t...)
 * 5. 新组件 init 函数中调用 g_widget_init()
 */

#ifndef __G_WIDGET
#define __G_WIDGET

#include "GAlign.h"
#include "Screen.h"
#include "config.h"

typedef struct _GWidget GWidget;
#define G_WIDGET(any) ((GWidget *)(any))

/**
 * @brief GWidget 自己实现描述自身宽高，注意自身宽高不包括 margin
 */
typedef uint16_t (*GWidgetNumsCallback)(GWidget *);

#define G_WIDGET_NUMS_CALLBACK(any) ((GWidgetNumsCallback)(any))

/**
 * @brief GWidget 自己实现 Renderable
 * @param startX 传入起始渲染位置 X
 * @param startY 传入起始渲染位置 Y
 * @param endX 传入渲染结束位置 X
 * @param endY 传入渲染结束位置 Y
 */
typedef void (*GWidgetRender)(GWidget *self,
                              uint16_t startX,
                              uint16_t startY,
                              uint16_t endX,
                              uint16_t endY);

#define G_WIDGET_RENDER(any) ((GWidgetRender)(any))

struct _GWidget
{
        /* 小部件的下一个实例 */
        GWidget *next;
        /* 获取小部件的最小宽度回调，不要自己加 margin 值 */
        GWidgetNumsCallback get_min_width;
        /* 获取小部件的最小高度回调，不要自己加 margin 值 */
        GWidgetNumsCallback get_min_height;
        /**
         * @brief 小部件渲染回调，需要小部件自己实现。操作步骤：
         * 
         * 一、实体小部件渲染步骤：
         * 1. 通过 g_align_scale 方法重新计算 startX、startY；
         * 2. startX、startY 加上 margin 后取得小部件的实际渲染起始坐标；
         * 3. 调用 screen_set_draw_area 指定实际绘制起点、终点；
         * 4. 根据像素点个数调用 screen_draw_pixel
         * 
         * 二、容器小部件渲染步骤：
         * 1. 渲染容器自身；
         * 2. 计算容器内需要分配给每个小部件的大小
         * 3. 对子部件调用 g_widget_render
         *
         * 三、需要判断：
         * 1. is_update，渲染前判断其若未更新，则渲染小部件，
         *    渲染结束时将其置为 true
         * 2. force_render，若为 true，则请强制刷新小部件
         */
        GWidgetRender render;
        /* 小部件的对齐方式，默认为 G_ALIGN_CENTER */
        GAlign align;
        /* 小部件周围的间距，默认为 0 */
        uint8_t margin;
        /* 小部件是否已刷新 */
        bool is_update;
        /* 小部件是否正在渲染中 */
        bool is_rendering;
        /* 是否强制刷新一次 */
        bool force_render;
};

extern void g_widget_init(GWidget *           self,
                          GWidgetNumsCallback width_cb,
                          GWidgetNumsCallback height_cb,
                          GWidgetRender       render);

/**
 * @brief 设置小部件的对齐方式
 */
extern void g_widget_set_align(GWidget *self, GAlign align);

/**
 * @brief 设置小部件的周围间距
 */
extern void g_widget_set_margin(GWidget *self, uint8_t margin);

/**
 * @brief 获取小部件的最小宽度
 */
extern uint16_t g_widget_get_min_width(GWidget *self);

/**
 * @brief 获取小部件的最小高度
 */
extern uint16_t g_widget_get_min_height(GWidget *self);

/**
 * @brief 在某一范围内渲染小部件
 */
extern void g_widget_render(GWidget *self,
                            uint16_t startX,
                            uint16_t startY,
                            uint16_t endX,
                            uint16_t endY);

/**
 * @brief 设置该 GWidget 强制渲染一次
 * @param self GWidget 对象
 */
extern void g_widget_set_force_render(GWidget *self);

#endif
